package vg.modules.filter;


import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import vg.modules.filter.components.ActionPanel;
import vg.services.data_base_manager.data.graph.Attribute;
import vg.services.data_base_manager.data.graph.Edge;
import vg.services.data_base_manager.data.graph.Graph;
import vg.services.data_base_manager.data.graph.Vertex;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.main_manager.MainManager;
import vg.services.plugin_manager.event_and_request.request.UIRequestAddTab;
import vg.services.plugin_manager.event_and_request.request.UIRequestReplaceCurrentTab;
import vg.services.plugin_manager.realization.PluginParameter;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.ConditionalPanel;

/**
 * This class is a filter for vertexes and edges.
 * @author tzolotuhin
 */
public class FilterPanel extends JFrame implements IFilterPanel {
	private static final long serialVersionUID = 1L;
	// init
	private boolean init = false;
	// Big components of interface
	private ConditionalPanel vertexConditionalPanel;
	private ConditionalPanel edgeConditionalPanel;
	private ActionPanel actionPanel;
	// Context
	private AGraphView currentView = null;
	private ArrayList<Integer>vertexIdsInCurrentView;
	private ArrayList<Integer>edgeIdsInCurrentView;
	// Other interface components
	private JCheckBox vertexConditionCheckBox, edgeConditionCheckBox;
	// Mutex
	private final Object theObjMutex = new Object(); // mutex for this object 
	// Small components of interface
	private JButton actionButton;
	private JButton cancelButton;
	// Modes
	private int actionMode = IFilterPanel.DEF_ACTION_MODE_3;
	//Other
	private final PluginParameter parameter;
	//Window settings
	private final static String DEF_WINDOWS_SIZE_X = "FILTER_PLUGIN_WindowSizeX";
	private final static String DEF_WINDOWS_SIZE_Y = "FILTER_PLUGIN_WindowSizeY";
	private final static String DEF_WINDOWS_POS_X = "FILTER_PLUGIN_WindowPosX";
	private final static String DEF_WINDOWS_POS_Y = "FILTER_PLUGIN_WindowPosY";
	private Integer windowSizeX, windowSizeY;
	private Integer windowPosX, windowPosY;
	/**
	 * Constructor.
	 * @param title - title of frame.
	 * @param parameter - connection with model and user interface.
	 */
	public FilterPanel(final String title, final PluginParameter parameter) {
		super(title);
		// save input parameters
		this.parameter = parameter;
	}
	/**
	 * This method changes current view.
	 * @param igv - current view.
	 */
	public void changeView(final AGraphView igv) {
		synchronized (theObjMutex) {
			this.currentView = igv;
			if(igv == null) {
				this.vertexConditionalPanel.setAttributes(null, null);
				this.edgeConditionalPanel.setAttributes(null, null);
				return;
			}
			// find all attributes in current selection subgraph
			Graph ssg = igv.getGraphInformation().getSelectionSubGraph();
			if(ssg == null) {
				this.vertexConditionalPanel.setAttributes(null, null);
				this.edgeConditionalPanel.setAttributes(null, null);
				return;			
			}
			// VERTICES
			this.vertexIdsInCurrentView.clear();
			Map<String, Set<String>>dataAttributesForVertices = new HashMap<String, Set<String>>();
			for(Vertex bufVertex : ssg.getVertices()) {
				this.vertexIdsInCurrentView.add(bufVertex.getStorableId());
				List<Attribute>sal = bufVertex.getAttributes();
				if(sal != null) {
					for(Attribute bufAttr : sal) {
						if(bufAttr.getName() != null && bufAttr.getValue() != null) {
							Set<String>value = dataAttributesForVertices.get(bufAttr.getName());
							if(value == null) {
								value = new HashSet<String>();
								dataAttributesForVertices.put(bufAttr.getName(), value);
							}
							value.add(bufAttr.getValue());
						} else {
							MainManager.log.printError("[" + this.getClass().getName() + ".changeView]" + " [BAD] Error in filter panel. Attribute name or attribute value = null(Vertex)");
						}
					}
				}
			} // end of vertex cycle
			this.vertexConditionalPanel.setAttributes(dataAttributesForVertices, null);
			// EDGES
			this.edgeIdsInCurrentView.clear();
			Map<String, Set<String>>dataAttributesForEdges = new HashMap<String, Set<String>>();
			for(Edge bufEdge : ssg.getEdges()) {
				this.edgeIdsInCurrentView.add(bufEdge.getStorableId());
				List<Attribute>sal = bufEdge.getAttributes();
				if(sal != null) {
					for(Attribute bufAttr : sal) {
						if(bufAttr.getName() != null && bufAttr.getValue() != null) {
							Set<String>value = dataAttributesForEdges.get(bufAttr.getName());
							if(value == null) {
								value = new HashSet<String>();
								dataAttributesForEdges.put(bufAttr.getName(), value);
							}
							value.add(bufAttr.getValue());
						} else {
							MainManager.log.printError("[" + this.getClass().getName() + ".changeView]" + " [BAD] Error in filter panel. Attribute name or attribute value = null(Edge)");
						}
					}
				}
			} // end of edge cycle
			this.edgeConditionalPanel.setAttributes(dataAttributesForEdges, null);
		}
	}
	public void setActionMode(int aMode) {
		synchronized (this.theObjMutex) {
			this.actionMode = aMode;
		}
	}
	
	public void open() {
		synchronized (theObjMutex) {
			if(!init) {
				if(SwingUtilities.isEventDispatchThread()) {
					init();
					this.setVisible(true);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (theObjMutex) {
								init();
								FilterPanel.this.setVisible(true);
							}
						}
					});
				}
			} else {
				if(SwingUtilities.isEventDispatchThread()) {
					this.setVisible(true);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (theObjMutex) {
								FilterPanel.this.setVisible(true);
							}
						}
					});
				}				
			}
		}
	}
	
	/**
	 * This method updates ui theme of filter plugin.
	 */
	public void updateUITheme() {
		synchronized (theObjMutex) {
			if(init) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						SwingUtilities.updateComponentTreeUI(FilterPanel.this);
					}
				});
				this.vertexConditionalPanel.updateUITheme();
				this.edgeConditionalPanel.updateUITheme();
			}
		}
	}
//=============================================================================
//-----------------PRIVATE METHODS---------------------------------------------
	private void init() {
		this.init = true;
		//---------------------------------------
		this.vertexConditionalPanel = new ConditionalPanel();
		this.edgeConditionalPanel = new ConditionalPanel();
		//---------------------------------------
		this.vertexConditionCheckBox = new JCheckBox("Conditions for vertices:");
		this.vertexConditionCheckBox.setSelected(true);
		this.edgeConditionCheckBox = new JCheckBox("Conditions for edges:");
		this.edgeConditionCheckBox.setSelected(true);
		//---------------------------------------
		this.actionPanel = new ActionPanel(this);
		this.vertexIdsInCurrentView = new ArrayList<Integer>();
		this.edgeIdsInCurrentView = new ArrayList<Integer>();
		// creating of window
		this.loadWindowOptions();
		this.setSize(this.windowSizeX, this.windowSizeY);
		this.setLocation(this.windowPosX, this.windowPosY);
		this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
		this.setLayout(new GridBagLayout());
		this.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				super.componentResized(e);
			    Component c = (Component)e.getSource();
		        Integer sizeX = c.getSize().width;
		        Integer sizeY = c.getSize().height;
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, sizeX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, sizeY.toString());
			}
			public void componentMoved(ComponentEvent e) {
				super.componentMoved(e);
			    Component c = (Component)e.getSource();
		        Integer posX = c.getLocation().x;
		        Integer posY = c.getLocation().y;
		        MainManager.config.setProperty(DEF_WINDOWS_POS_X, posX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_POS_Y, posY.toString());
			}
		});
		// package of interface
		GridBagConstraints gbc = null;
		// top panel in split pane
		JPanel topPanel = new JPanel(new GridBagLayout());
		gbc = new GridBagConstraints(0,0,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0),  0,0);
		topPanel.add(this.vertexConditionCheckBox, gbc);
		
		gbc = new GridBagConstraints(0,1,  1,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
		topPanel.add(this.vertexConditionalPanel.getView(), gbc);
		// bottom panel in split pane
		JPanel bottomPanel = new JPanel(new GridBagLayout());
		gbc = new GridBagConstraints(0,0,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0),  0,0);
		bottomPanel.add(this.edgeConditionCheckBox, gbc);
		
		gbc = new GridBagConstraints(0,1,  1,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
		bottomPanel.add(this.edgeConditionalPanel.getView(), gbc);
		// create split pane
		final JSplitPane vertexAndEdgeSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
		vertexAndEdgeSplitPane.setResizeWeight(0.5);
		vertexAndEdgeSplitPane.setDividerSize(8);
		gbc = new GridBagConstraints(0,0,  2,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
		this.add(vertexAndEdgeSplitPane, gbc);
		
		gbc = new GridBagConstraints(0,1,  2,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0);
		this.add(this.actionPanel.getView(), gbc);
		
		this.cancelButton = new JButton("Cancel");
		this.cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						FilterPanel.this.setVisible(false);
					}
				});
			}
		});
		gbc = new GridBagConstraints(0,2,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0);
		this.add(this.cancelButton,gbc);

		this.actionButton = new JButton("Do");
		this.actionButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				action();
			}
		});
		gbc = new GridBagConstraints(1,2,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0);
		this.add(this.actionButton,gbc);
	}
	/**
	 * This method creates new tab with out subgraph.
	 */
	private void action() {
		synchronized (this.theObjMutex) {
			// search id of graph in model
			if(currentView == null) return;
			int graph_id = currentView.getLowLevelGraphEditor().getGraphId();
			// build request to database
			final Set<Integer> vertex_list = DataBaseWorker4Filter.get_vertex_ids(vertexConditionalPanel.getConditionPanelData(), parameter.model, graph_id);
			final Set<Integer> edge_list = DataBaseWorker4Filter.get_edge_ids(edgeConditionalPanel.getConditionPanelData(), parameter.model, graph_id);
			/////////////////////////////////////
			// ACTION
			/////////////////////////////////////
			switch(actionMode) {
				case DEF_ACTION_MODE_1: {
					Thread t = new Thread(new Runnable() {
						@Override
						public void run() {
							AGraphView igv = MainManager.graphViewManager.newGraphView(currentView.getGraphInformation().getGraph());
							igv.getHighLevelGraphEditor().setInvisibleAllVerticesBut(vertex_list);
							igv.getHighLevelGraphEditor().setInvisibleAllEdgesBut(edge_list);
							UIRequestAddTab requestOpenSubGraph = new UIRequestAddTab(igv);
							parameter.userInterface.addRequest(requestOpenSubGraph);
						}
					});
					t.start();
					break;
				}
				case DEF_ACTION_MODE_2: {
					Thread t = new Thread(new Runnable() {
						@Override
						public void run() {
							AGraphView igv = MainManager.graphViewManager.newGraphView(currentView.getGraphInformation().getGraph());
							igv.getHighLevelGraphEditor().setInvisibleAllVerticesBut(vertex_list);
							igv.getHighLevelGraphEditor().setInvisibleAllEdgesBut(edge_list);
							UIRequestReplaceCurrentTab requestOpenSubGraph = new UIRequestReplaceCurrentTab(igv, null);
							parameter.userInterface.addRequest(requestOpenSubGraph);
						}
					});
					t.start();
					break;
				}
				case DEF_ACTION_MODE_3: {
					if(this.currentView != null) {
						this.currentView.getHighLevelGraphEditor().setSelectionElements(vertex_list, edge_list);
					}
					break;
				}
			}
		}
	}
	
	/**
	 * Load window options.
	 */
	private void loadWindowOptions() {
		String wSizeStrX = MainManager.config.getProperty(DEF_WINDOWS_SIZE_X);
		String wSizeStrY = MainManager.config.getProperty(DEF_WINDOWS_SIZE_Y);
		String wPosStrX = MainManager.config.getProperty(DEF_WINDOWS_POS_X);
		String wPosStrY = MainManager.config.getProperty(DEF_WINDOWS_POS_Y);
		//set size X(width of window)
		if(wSizeStrX == null) {
			this.windowSizeX = 800;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
		} else {
			try {
				this.windowSizeX = new Integer(wSizeStrX);
			} catch(NumberFormatException ex) {
				this.windowSizeX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
			}
		}
		//set size Y(height of window)
		if(wSizeStrY == null) {
			this.windowSizeY = 600;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());			
		} else {
			try {
				this.windowSizeY = new Integer(wSizeStrY);
			} catch(NumberFormatException ex) {
				this.windowSizeY = 600;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());
			}			
		}
		//set position X
		if(wPosStrX == null) {
			this.windowPosX = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
		} else {
			try {
				this.windowPosX = new Integer(wPosStrX);
			} catch(NumberFormatException ex) {
				this.windowPosX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
			}
		}
		//set position Y
		if(wPosStrY == null) {
			this.windowPosY = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
		} else {
			try {
				this.windowPosY = new Integer(wPosStrY);
			} catch(NumberFormatException ex) {
				this.windowPosY = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
			}
		}
	} 
}
